﻿using System;
using System.Collections.Generic;

namespace TokenUtil
{
   class Parameters
   {
      static char[] _argPrefixes = new char[] { '-', '/' };

      Dictionary<string, Parameter> _parameterSetTemplate;

      List<string> _parameterSetMarkers;

      List<Dictionary<string, Parameter>> _parameterSets = new List<Dictionary<string, Parameter>>();
      public List<Dictionary<string, Parameter>> ParameterSets
      {
         get { return _parameterSets; }
      }

      List<Action<Dictionary<string, Parameter>>> _validators;

      /// <summary>
      /// The constructor sets up the parameter class with:
      /// 1. A template of possible paraemters
      /// 2. Flags which are arguments that mark the beginning of a parameter set
      /// 3. Validators which are functions applied to parameter sets to make sure they are sane
      /// </summary>
      /// <param name="parameterTableTemplate">A table that contains all possible parameter flags</param>
      /// <param name="parameterSetMarkers">List of argument flags that mark the beginning of a parameter set</param>
      /// <param name="validators">List of validators which are functions applied to parameter sets to make sure they are sane</param>
      public Parameters(
         Dictionary<string, Parameter> parameterTableTemplate,
         List<string> parameterSetMarkers,
         List<Action<Dictionary<string, Parameter>>> validators)
      {
         _parameterSetTemplate = parameterTableTemplate;
         _parameterSetMarkers = parameterSetMarkers;
         _validators = validators;
      }

      /// <summary>
      /// Process the command line arguments.
      /// </summary>
      /// <param name="args">Command line arguments from main()</param>
      public void Process(string[] args)
      {
         if (null == args) throw new ArgumentNullException("args");

         Dictionary<string, Parameter> parameterTable = null;
         for (int i = 0; i < args.Length; i++)
         {
            if (!IsArgFlag(args[i])) continue;

            string flag = args[i].TrimStart(_argPrefixes).ToLowerInvariant();

            if (_parameterSetMarkers.Contains(flag))
            {
               parameterTable = CreateParameterSet();
               _parameterSets.Add(parameterTable);
            }

            Parameter parameter;
            //if (this.TryGetValue(flag, out parameter))
            if (parameterTable.TryGetValue(flag, out parameter))
            {
               parameter.IsSet = true;

               if (null != parameter.Type)
               {
                  if ((i + 1) >= args.Length)
                     throw new InvalidOperationException("Expecting argument value after " + args[i] + ".");

                  parameter.Value = ProcessArgValue(args[i], args[i + 1], parameter.Type);
               }
            }
         }

         //Validate parameters
         foreach (Dictionary<string, Parameter> parameterSet in _parameterSets)
            foreach (Action<Dictionary<string, Parameter>> validator in _validators)
               validator(parameterSet);
      }

      /// <summary>
      /// Process and validate an argument value.
      /// </summary>
      /// <param name="flag">The potential argument flag</param>
      /// <param name="value">The potential argument value</param>
      /// <param name="type">Expected argument type</param>
      /// <returns>Processed argument value</returns>
      Object ProcessArgValue(string flag, string value, Type type)
      {
         //Make sure the argument value does not start with an argument prefix.
         if (IsArgFlag(value)) throw new InvalidOperationException("Expecting argument value after " + flag + ".");

         if (typeof(string) == type)
            return value;
         else if (typeof(int) == type)
            return int.Parse(value);
         else if (typeof(Uri) == type)
            return new Uri(value);
         else
            throw new InvalidOperationException("Unsupported type");
      }

      /// <summary>
      /// Is the string an argument flag.
      /// </summary>
      /// <param name="str">Potential argument flag</param>
      /// <returns>True if the string is an argument flag false otherwise</returns>
      bool IsArgFlag(string str)
      {
         foreach (char c in _argPrefixes)
            if (str[0] == c) return true;

         return false;
      }

      /// <summary>
      /// Creates a table that contains a parameter set from the parameter set template.
      /// </summary>
      /// <returns>Parameter set table</returns>
      Dictionary<string, Parameter> CreateParameterSet()
      {
         Dictionary<string, Parameter> parameterTable = new Dictionary<string, Parameter>();

         foreach (KeyValuePair<string, Parameter> keyValue in _parameterSetTemplate)
            parameterTable.Add(keyValue.Key, keyValue.Value.GetCopy());

         return parameterTable;
      }
   }
}
